//=====================================================================================
/** \file weather5.cc
 *  This file contains a program which runs a simple weather station using the 
 *  following components:
 *    \li Sensirion SHT15 temperature/humidity sensor, or DS1822 temperature sensor
 *    \li MaxStream 9XStream radio modem
 *    \li AAG One-Wire wind measurement unit containing a DS2423 and DS2450, or a
 *        hacked sensor with a Hall effect switch counting rotations
 *    \li If used, a wind vane based on a US Digital MA3 magnetic analog 'encoder'
 *    \li If used, a voltage divider used to check the supply voltage
 *    \li An AVR processor such as an ATmega32 or ATmega324
 *
 *  Task list
 *    \li Wind Speed:  Task to check the wind speed sensor, compute speed, and update
 *        data for maximum and average wind speed over a time interval
 *    \li Wind Direction:  Task to check wind vane and update data used to compute
 *        average wind direction over a time interval
 *    \li Temperature:  Task to check a DS1820/22 temperature sensor
 *    \li Radio:  Task save up and transmit a set of readings periodically
 *
 *  Revisions
 *    \li 07-19-2007 JRR Original file tests radio modem
 *    \li 07-21-2007 JRR Temperature/humidity sensor added
 *    \li 08-07-2007 JRR Version for battery/solar use written
 *    \li 11-03-2007 JRR Reporting slowed down a bunch
 *    \li 12-21-2007 JRR Wrote a version for the One-Wire wind sensor
 *    \li 12-26-2007 JRR Linked to the stl_task scheduler
 *    \li 07-03-2007 JRR Modified for use with the Swoop V0.6C custom board
 *    \li 03-31-2009 JRR Version deployed to site with C3 anemometers and MA3 vane;
 *                       calibration of vane: (-72,105) is pointing away from the C3
 *    \li 09-19-2009 JRR Added lead-acid battery charge controller task
 */
//======================================================================================

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

#include "m9xstream.h"						// MaxStream radio header plus RS232, etc.
#include "avr_sht15.h"						// Sensirion SHT15 sensor header
#include "queue.h"							// Stores data in queues
#include "avr_1wire.h"						// Dallas/Maxim One-Wire Interface
#include "avr_ds1822.h"						// Dallas temperature sensor
#include "global_debug.h"					// For global debugging serial port
#include "stl_timer.h"						// Timer for task scheduling
#include "stl_task.h"						// Main task class
#include "task_temperature.h"				// Task for reading temperature sensor
#include "task_wind.h"						// Task for reading the wind sensors
#include "task_temphumid.h"					// Task reads temperature/humidity sensor
#include "task_battery.h"					// Task which monitors and charges battery
#include "task_radio.h"


//-------------------------------------------------------------------------------------
/** This function activates the watchdog timer. The timer is set to a fairly long time
 *  interval, in this case about 2 seconds. 
 */

void activate_watchdog_timer (void)
{
	wdt_enable (WDTO_2S);
}


//-------------------------------------------------------------------------------------
/** This function deactivates the watchdog timer. It is used when the program starts
 *  up to ensure that the timer doesn't cause re-resets after the watchdog has reset
 *  the processor, in case the timer wasn't deactivated by the first reset.
 */

void disable_watchdog_timer (void)
{
	MCUSR = 0;
	wdt_disable ();
}


//=====================================================================================
/** This main method sets up the weather station driver and task classes, then runs the
 *  tasks in a loop. This program's tasks all have pretty lax timing requirements, and 
 *  they are all run via cooperative multitasking by calls to schedule() in the loop. 
 *  The main loop also puts the processor to sleep for a while during times when the 
 *  tasks don't need to run right away. 
 */

int main ()
{
	unsigned char hrs, mins, secs;			// Storage for the time of day
	time_stamp time_to_run (0, 0, 0, 0, 0);	// Holds a measurement of the current time

	// Make sure the watchdog timer is disabled during the startup sequence; it will
	// be activated once things are up and running, when the task scheduling loop is
	// able to deactivate the timer periodically
	disable_watchdog_timer ();

	// Create the radio modem with (adding the debug port here):
	// CTS out/in port, DDR, mask; sleep port, DDR, bitmask; UART number, baud
	sei ();
	#ifdef TEST_C3
		rs232 a_radio (9600, 1);
	#else
		m9XStream a_radio (PORTD, PIND, DDRD, 0x10, PORTD, DDRD, 0x20, 0, 9600);
		// Find the address from the radio modem, then say hello
		int my_address = a_radio.get_my_address ();
		int to_address = a_radio.get_to_address ();
// 		a_radio << "Addr: 0x" << hex << my_address << " -> 0x" << to_address << dec ;
	#endif
	a_radio << endl << "PolyWind WX Rev " << THE_DATE << endl;

	#ifdef SERIAL_DEBUG	
		set_glob_debug_port (&a_radio);
	#endif
	GLOB_DEBUG (endl << "Debugging for Weather Station" << endl);

	#ifdef WINDY_REPEATER
		a_radio << " (Repeater)";
	#endif
	a_radio << endl;

	// Set up a task timer so that tasks can figure out when to run
	task_timer the_timer;

	// An A/D converter will be needed for battery charging or an MA3 wind vane
	#if defined USE_VANE_MA3 || defined USE_CHARGE_CTL
		adc_10bit my_adc;
	#endif

	// If needed, create a wind speed and/or direction measuring task
	#if defined (USE_VANE_MA3)
		task_wind* wind_task = new task_wind (the_timer, time_stamp (0, 0, 0, 10), 
											  &my_adc);
	#else
		task_wind* wind_task = new task_wind (the_timer, time_stamp (0, 0, 0, 10), 
											  NULL);
	#endif

	// Create a solar charge controller task to keep a lead-acid battery full
	// Parameters are A/D driver and channel, port and DDR and bit for shunt control,
	// task timer, and interval time
	#ifdef USE_CHARGE_CTL
		task_battery* battery_task = new task_battery (&my_adc, 5, PORTD, DDRD, 6,
												the_timer, time_stamp (0, 0, 0, 1));
	#else
		task_battery* battery_task = NULL;
	#endif

	// If using the DS1822 temperature sensor, create and set up its 1-wire bus
	#ifdef USE_DS1822
		avr_1wire therm_1wire (PINC, PORTC, DDRC, 0x20);
		therm_1wire.search ();
		a_radio << "DS1822 1W:" << endl;
		therm_1wire.show_devices (&a_radio);
		avr_ds1822 the_1822 (&therm_1wire);
		the_1822.find_by_type ();
		task_temperature* thermo_task = new task_temperature (&the_1822, 
															   time_stamp (0, 0, 1));
		task_temphumid* humid_task = NULL;
	#else
		// Create object for the SHT15 on Port C with clock on PC0, data on PC1; make a
		// task which reads the current temperature and humidity periodically
		#ifdef USE_SHT15
			avr_sht15 the_sht15 (0x01, 0x02);
			the_sht15.reset ();  
			task_temphumid* thermo_task = new task_temphumid (&the_sht15, 
														time_stamp (0, 0, 1), NULL);
			task_temperature* thermo_task = NULL;
		// If the part below compiles, neither the DS1822 nor the SHT15 is being used
		#else
			task_temphumid* humid_task = NULL;
			task_temperature* thermo_task = NULL;
		#endif
	#endif

	// Create a task which periodically sends out data over the radio. The task will
	// start in a few seconds (not immediately) 
	#ifndef TEST_C3
		uint8_t radio_delay = (uint8_t)(my_address & 0x000F) * 2;
		task_radio* radio_task = new task_radio (&a_radio, wind_task, humid_task, 
			thermo_task, battery_task, the_timer, time_stamp (0, 0, 5, radio_delay));
	#else
		GLOB_DEBUG (PMS ("  Showing wind pulses counted every 10 seconds.") << endl 
			<< PMS ("  There are 2 pulses per revolution of the anemometer.") << endl
			);
	#endif

	///////////////////////////////////////////////////////////////////////////////////
// 	task_radio* radio_task = new task_radio (&a_radio, &the_timer, wind_task, 
// 		humid_task, thermo_task, battery_task, time_stamp (0, 0, 0, 10));
	///////////////////////////////////////////////////////////////////////////////////

	// Enable interrupts. This must be done so that the timer interrupt can be used
	// to run tasks on time and to wake up the processor if it has been sleeping
	sei ();

	// Synchronize the run times of the tasks with the main task timer; the tasks are
	// delayed a little bit so that the radios don't talk over each other
	time_to_run = the_timer.get_time_now ();
	#ifndef TEST_C3
		time_to_run += time_stamp (0, 0, 0, radio_delay);
	#endif
	#ifdef USE_C3
		wind_task->set_next_run_time (time_to_run);
	#endif
	#ifdef USE_CHARGE_CTL
		battery_task->set_next_run_time (time_to_run);
	#endif
	#if (defined USE_SHT15 || defined USE_DS1822)
		thermo_task->set_next_run_time (time_to_run);
	#endif
	#ifndef TEST_C3
		radio_task->set_next_run_time (time_to_run);
	#endif

	// Activate the watchdog timer, which will reset the processor if anything gets 
	// stuck
	activate_watchdog_timer ();

	// Run the main task scheduling loop, in which each task's schedule() method runs
	while (1)
	{
		// Give each task an opportunity to run if it needs to
		#ifdef USE_C3
			wind_task->schedule ();
		#endif
		#if (defined USE_SHT15 || defined USE_DS1822)
			thermo_task->schedule ();
		#endif
		#ifdef USE_CHARGE_CTL
			battery_task->schedule ();
		#endif
		#ifndef TEST_C3
			radio_task->schedule ();
		#endif

		// Reset the watchdog timer so it doesn't cause the processor to reset
		wdt_reset ();

		// Go to sleep until interrupted by the time-of-day timer. We use "Power-save"
		// mode, in which Timer 2 keeps running but almost everything else stops, or
		// "Idle" mode, in which the CPU clock stops but the peripherals are powered
// 		 #if (defined USE_SHT15 || defined USE_DS1822)
// 			 if (!wind_task->ready () && !radio_task->ready () 
// 				 && !thermo_task->ready ())
// 		 #else
// 			 if ((!wind_task->ready ()) && (!radio_task->ready ()))
// 		 #endif
// 			 {
// 			 set_sleep_mode (/* SLEEP_MODE_PWR_SAVE */ SLEEP_MODE_IDLE);
// 			 sleep_enable ();
// 			 sleep_cpu ();
// 			 sleep_disable ();				   // The CPU wakes up here
// 			 }
	}

	return (0);
}
